package com.bruce.shop.commons.utils;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

/**
 * @author BruceXia
 * @version 1.0.0
 * @description 连接到数据库
 * @date 2022/8/2917:22
 */
public class DBUtils {
    public static Connection conn;

    public static PreparedStatement statement;

    public static ResultSet rs;

    public static Properties p=new Properties();
    public static Properties p2=new Properties();

    //创建一个本地线程对象

    private static ThreadLocal<Connection> ct = new ThreadLocal<>();

    static {
        //静态代码块加载配置文件
         p=PropertiesUtils.loadProperties("jdbc.properties");
         p2=PropertiesUtils.loadProperties(("orm.properties"));
    }

    /**
     * 创建连接对象的方法
     * @return
     */
    public static Connection getConnection(){
        try {
            //加载数据库驱动
            Class.forName(p.getProperty("driver"));
            conn= DriverManager.getConnection(p.getProperty("url"),p.getProperty("username"),p.getProperty("password"));
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return conn;
    }


    /**
     * 事务开始
     */
    public static void begin(){
        Connection conn=DBUtils.getConnection();
        try {
            //关闭自动事务，开启手动事务
            conn.setAutoCommit(false);
            ct.set(conn);
        } catch (SQLException e) {
            e.printStackTrace();
        }




    }

    /**
     * 事务提交
     */
    public static void commit(){
        //从本地线程对象中获取这个事务的连接对象
       conn= ct.get();
        try {
            //提交事务
            conn.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }


    /**
     * 事务回滚
     */
    public static void rollback(){
        conn=ct.get();
        try {
            conn.rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    /**
     * 增删改的方法
     * @param sql
     * @param args
     * @return
     */
    public static int write(String sql,Object...args){
        //手动提交
        conn=ct.get();
        if (conn==null){
            //新增一个自动提交的
            conn=getConnection();
        }

        int result=0;
        //通过预编译执行对象将sql语句代入数据库中执行
        try {
            statement=conn.prepareStatement(sql);

            //用预编译执行对象调用占位符传参
            for (int i=0;i<args.length;i++){
                statement.setObject(i+1,args[i]);
            }

            //将执行后的结果返回
            result=statement.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            close(statement);
            //这里不能关conn对象，因为事务的处理还需要在同一个conn对象中

        }
        return result;

    }

    /**
     * 查询单个语句
     * @param sql
     * @param clazz
     * @param args
     * @param <T>
     * @return
     */
    public static <T> T selectOne(String sql,Class<T> clazz,Object...args){

        List<T> list=selectAll(sql,clazz,args);
        if (!list.isEmpty() && list.size()>0){
            return list.get(0);
        }
        return null;

    }

    /**
     * 查询多条数据
     * 我们要返回的是一个载满结果集数据的实体类的对象集合，这个对象里的属性和这个结果集的列名一一对应，如果有不对应的应该在orm配置文件中说明
     * 让它们实现一一映射关系
     * 首先获得整个结果集，然后通过反射获得泛型类的所有属性，然后遍历所有属性，修改私有权限，以属性名为条件调用结果集的setObject(name)去里面找对应
     * 的值，然后将值赋到这个泛型类的对象中
     * @param sql
     * @param clazz
     * @param args
     * @param <T>
     * @return
     */
    public static <T> List<T> selectAll(String sql, Class<T> clazz, Object...args){
        List<T> list=new ArrayList<>();
        conn=getConnection();

        try {
            statement=conn.prepareStatement(sql);

            for (int i=0;i<args.length;i++){
                statement.setObject(i+1,args[i]);
            }

            rs=statement.executeQuery();

            while (rs.next()){
                    //通过反射获得类的实例化对象
                    T t=clazz.newInstance();

                    Field[] fd=t.getClass().getDeclaredFields();
                    for (int i=0;i<fd.length;i++){
                        //修改访问权限
                        fd[i].setAccessible(true);
                        //获得当前被遍历属性的名字
                        String name=fd[i].getName();

                        Object valu=null;

                        try {
                            //在结果集中寻找名字对应的属性
                            valu= rs.getObject(name);
                        } catch (SQLException e) {
                            //没有找到就启动orm配置文件中的映射关系去获得对应数据库中的列名
                            if (p2.getProperty(name)!=null){
                                String name2=p2.getProperty(name);
                                //然后再通过这个列名去结果集中获得对应的值
                                valu=rs.getObject(name2);
                            }

                        }
                        //将列的值赋进其对应的属性中
                        try {
                            fd[i].set(t,valu);
                        } catch (IllegalArgumentException e) {
                           //将字符串转成数组存入hobby集合
                            String[] hobby=valu.toString().split(",");
                            fd[i].set(t, Arrays.asList(hobby));
                        }
                    }

                    list.add(t);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }finally {
            close(rs,statement,conn);
        }
        return list;
    }


    /**
     * 关对象
     * @param args
     */
    public static void close(AutoCloseable...args){
        for (AutoCloseable arg : args) {
            if (arg!=null){
                try {
                    arg.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

    public static Integer readCount(String sql) {
        Integer count=0;
        conn=getConnection();

        try {
            statement=conn.prepareStatement(sql);
            rs=statement.executeQuery();
            if (rs.next()){
                count=rs.getInt(1);
            }


        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            close(rs,statement,conn);
        }

        return count;


    }
}
